<?php
/**
 * Created by JetBrains PhpStorm.
 * User: dodasso
 * Date: 8/3/13
 * Time: 8:20 PM
 * To change this template use File | Settings | File Templates.
 */

namespace session;

require_once(APP_ROOT . '/modules/log/log.php');
require_once(APP_ROOT . '/modules/log/logCommon.php');
require_once(APP_ROOT . '/modules/common/Common.php');
require_once(APP_ROOT . '/modules/common/Utils.php');
require_once(APP_ROOT . '/modules/common/Errors.php');
require_once(APP_ROOT . '/modules/persistance/DBConnector.php');
require_once(APP_ROOT . '/modules/persistance/SessionPersistance.php');
require_once(APP_ROOT . '/modules/persistance/UserPersistance.php');
require_once(APP_ROOT . '/modules/session/SessionDto.php');
require_once(APP_ROOT . '/modules/session/UserDto.php');

use log\Log;
use log\LogCommon;
use common\Utils;
use common\Errors;
use persistance\DBConnector;
use persistance\SessionPersistance;
use persistance\UserPersistance;
use session\SessionDto;

class SessionModule {
    /**
     * @var DBConnector
     */
    private $db;

    /**
     * @var SessionPersistance
     */
    private $sessionPersistance;

    /**
     * @var UserPersistance
     */
    private $userPersistance;

    /**
     * @var boolean
     */
    private $initialized;

    public function __construct() {
        Log::LOG_SESSION_FB();

        $this->db                 = null;
        $this->sessionPersistance = null;
        $this->userPersistance    = null;
        $this->initialized        = false;

        Log::LOG_SESSION_FE();
    }

    public function __destruct() {
        Log::LOG_SESSION_FB();
        Log::LOG_SESSION_FE();
    }

    public function init($db) {
        Log::LOG_SESSION_FB();

        if((is_null($db)) || !($db instanceof DBConnector)) {
            Log::LOG_SESSION(LogCommon::logTypeError, "Invalid parameters.");
            return false;
        }

        $this->db                 = $db;
        $this->userPersistance    = new UserPersistance();
        $this->sessionPersistance = new SessionPersistance();
        $this->userPersistance->init($db);
        $this->sessionPersistance->init($db);
        $this->initialized = true;

        Log::LOG_SESSION_FE();

        return true;
    }

    public function login($username, $password, $remoteAddress, $userAgent) {
        Log::LOG_SESSION_FB();

        if((!is_string($username)) || (!is_string($password)) ||
            (!is_string($remoteAddress)) || (!is_string($userAgent))) {
            Log::LOG_SESSION(LogCommon::logTypeError, "Invalid parameters.");
            return Utils::buildResponse(array('error'=>Errors::INTEGRAL_ERR_INVALID_PARAMETERS));
        }

        $rs = $this->userPersistance->findByUsername($username);
        if($rs['error'] === Errors::INTEGRAL_ERR_PERSISTANCE_NOT_FOUND) {
            Log::LOG_SESSION(LogCommon::logTypeError, "Unable to find user. Username: " .
                $username . ' [' . $rs['error'] . ']');
            return Utils::buildResponse(array('error'=>Errors::INTEGRAL_ERR_SESSION_USER_NOT_AUTHORIZED,
                                              'data'=>''));
        }
        if($rs['error'] !== Errors::INTEGRAL_ERR_NONE) {
            Log::LOG_SESSION(LogCommon::logTypeError, "Find user error. Username: " .
                $username . ' [' . $rs['error'] . ']');
            return Utils::buildResponse(array('error'=>Errors::INTEGRAL_ERR_SESSION_NOT_AUTHORIZED,
                                              'data'=>''));
        }

        $userDto = $rs['data'];
        if($userDto->password !== $password) {
            Log::LOG_SESSION(LogCommon::logTypeSecurity, 'Invalid password. ' . $password);
            return Utils::buildResponse(array('error'=>Errors::INTEGRAL_ERR_SESSION_INVALID_PASSWORD,
                                              'data'=>''));
        }

        $rs = $this->sessionPersistance->deleteSessionsOfUser($userDto);
        if($rs['error'] != Errors::INTEGRAL_ERR_NONE) {
            Log::LOG_SESSION(LogCommon::logTypeWarning, 'Unable to delete old sessions of user. User id. '
                . $this->userDto->id . ' [' . $rs['error'] . ']');
        }

        $sessionDto                  = new SessionDto();
        $sessionDto->key             = sha1(Utils::uuid());
        $sessionDto->remoteAddresses = $remoteAddress;
        $sessionDto->userAgent       = $userAgent;
        $sessionDto->userId          = $userDto->id;

        $rs = $this->sessionPersistance->createSession($sessionDto);
        if($rs['error'] != Errors::INTEGRAL_ERR_NONE) {
            return Utils::buildResponse(
                array('error'=>Errors::INTEGRAL_ERR_SESSION_UNABLE_TO_LOGIN));
        }

        if(!$this->phpInitSession()) {
            return Utils::buildResponse(
                array('error'=>Errors::INTEGRAL_ERR_SESSION_UNABLE_TO_LOGIN));
        }

        unset($sessionDto);
        $sessionDto = $rs['data'];
        $this->phpSessionInsert('sessionKey', $sessionDto->key);

        unset($rs['data']);
        $rs['data']['sessionKey'] = $sessionDto->key;

        Log::LOG_SESSION_FE();

        return Utils::buildResponse(array('error'=>Errors::INTEGRAL_ERR_NONE, 'data'=>$rs['data']));
    }

    public function logout($sessionKey) {
        Log::LOG_SESSION_FB();

        $this->phpCleanSession();

        if((!is_string($sessionKey))) {
            Log::LOG_SESSION(LogCommon::logTypeError, "Invalid parameters.");
            return Utils::buildResponse(array('error'=>Errors::INTEGRAL_ERR_INVALID_PARAMETERS));
        }

        $rs = $this->sessionPersistance->findByKey($sessionKey);
        if($rs['error'] != Errors::INTEGRAL_ERR_NONE) {
            Log::LOG_SESSION(LogCommon::logTypeError, 'Unable to find session. Key ' .
                $sessionKey . ' [' . $rs['error'] . ']');
            return Utils::buildResponse(
                array('error'=>Errors::INTEGRAL_ERR_SESSION_UNABLE_TO_LOGOUT));
        }

        $rs = $this->sessionPersistance->deleteSession($rs['data']);
        if($rs['error'] != Errors::INTEGRAL_ERR_NONE) {
            Log::LOG_SESSION(LogCommon::logTypeWarning, 'Unable to delete session. Key ' .
                $sessionKey . ' [' . $rs['error'] . ']');
        }

        Log::LOG_SESSION_FE();

        return Utils::buildResponse(array('error'=>Errors::INTEGRAL_ERR_NONE));
    }

    private function phpInitSession() {
        if(session_id() != '') {
            Log::LOG_COMMON(LogCommon::logTypeError,
                'Session already initialized: Session id: ' . session_id());
            return true;
        }

        if(!session_start()) {
            return false;
        };

        return true;
    }

    private function phpCleanSession() {
        session_unset();
        session_destroy();

        return true;
    }

    public function phpSessionInsert($key, $value) {
        $_SESSION[Utils::encrypt($key)] = Utils::encrypt($value);
    }

    public function phpSessionGet($key) {
        $encrypt = Utils::encrypt($key);
        if(!array_key_exists($encrypt, $_SESSION)) {
            return null;
        }

        $value = $_SESSION[$encrypt];
        $value = Utils::decrypt($value);

        return $value;
    }

    public function phpSessionRemove($key) {
        $encrypt = Utils::encrypt($key);
        unset($_SESSION[$encrypt]);
        session_commit();
    }

    public function phpSessionIsValid() {
        if(session_id() === '') {
            Log::LOG_COMMON(LogCommon::logTypeError,
                'Session not available.');
            return false;
        }

        $value = Utils::phpSessionGet('sessionKey');
        if((is_null($value)) || (empty($value))) {
            Log::LOG_COMMON(LogCommon::logTypeError,
                'Session key not available.');
            return false;
        }

        return true;
    }
}